package tools

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"github.com/astaxie/beego/logs"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"
)

type MyResponse struct {
	StatusCode int
	Body       []byte
	Headers    map[string]string
}

func NewMyResponse() *MyResponse {
	return &MyResponse{
		Headers: make(map[string]string, 1),
	}
}

func DefaultHeader() map[string]string {
	headers := make(map[string]string)
	headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36"
	return headers
}

/**
cookiejar : 支持浏览器方式处理cookies
*/
//var conn_http = GetHttpClient()

func GetHttpClient() *http.Client {

	conn_http := &http.Client{

		Transport: &http.Transport{
			Dial: func(netw, addr string) (net.Conn, error) {
				conn_http, err := net.DialTimeout(netw, addr, time.Second*30)
				if err != nil {
					fmt.Println("dail timeout", err)
					return nil, err
				}
				conn_http.SetDeadline(time.Now().Add(time.Second * 30))
				return conn_http, nil

			},
			MaxIdleConnsPerHost:   64,
			ResponseHeaderTimeout: time.Millisecond * 30000,
			DisableKeepAlives:     false,
		},
	}

	jar, err := cookiejar.New(nil)
	if err != nil {
		panic(err)
	}
	conn_http.Jar = jar

	return conn_http
}

func PostForm(targetUrl string, form map[string]string) *MyResponse {
	conn_http := GetHttpClient()

	formData := url.Values{}
	for k, v := range form {
		formData.Set(k, v)
	}

	resp, err := conn_http.PostForm(targetUrl, formData)

	if err != nil {
		fmt.Println("Err2:", err)
		return nil
	}

	return convertMyResponse(resp, &err)
}

func Post(url string, body string, contentType string) *MyResponse {
	conn_http := GetHttpClient()
	logs.Debug("Send Post: %s\n", url)
	logs.Debug(body)

	resp, err := conn_http.Post(url, contentType, strings.NewReader(body))

	if err != nil {
		logs.Error("Err2:", err, "body:", body)
		return nil
	}

	return convertMyResponse(resp, &err)
}

func Delete(url string) *MyResponse {
	logs.Debug("Send Get: %s\n", url)

	client := &http.Client{}

	req, err := http.NewRequest("DELETE", url, strings.NewReader(""))
	if err != nil {
		// handle error
	}

	resp, err := client.Do(req)
	defer close(resp)

	if resp == nil {
		return nil
	}

	return convertMyResponse(resp, &err)
}

func Get(url string) *MyResponse {
	conn_http := GetHttpClient()
	logs.Debug("Send Get: %s\n", url)

	resp, err := conn_http.Get(url)
	defer close(resp)

	if err != nil {

		retry := 3
		for i := 0; i < retry; i++ {
			resp, err = conn_http.Get(url)
			defer close(resp)
			if resp != nil && resp.StatusCode == 200 {
				break
			}
		}

		if resp == nil {
			logs.Error("Err2:", err)
			return nil
		}
	}

	return convertMyResponse(resp, &err)
}

func GetWithTimeout(url string, timeout time.Duration) *MyResponse {

	client := http.Client{
		Timeout: timeout,
	}
	resp, err := client.Get(url)
	defer close(resp)

	if err != nil {

		retry := 3
		for i := 0; i < retry; i++ {
			resp, err = client.Get(url)
			defer close(resp)
			if resp != nil && resp.StatusCode == 200 {
				break
			}
		}

		if resp == nil {
			logs.Error("Err2:", err)
			return nil
		}
	}

	return convertMyResponse(resp, &err)
}

func Get2(url string, headers map[string]string) *MyResponse {
	logs.Debug("Send Get: %s\n", url)

	client := &http.Client{}

	req, err := http.NewRequest("GET", url, strings.NewReader(""))
	if err != nil {
		// handle error
	}

	for k, v := range headers {
		req.Header.Set(k, v)
	}

	resp, err := client.Do(req)
	defer close(resp)

	if resp == nil {
		return nil
	}

	return convertMyResponse(resp, &err)
}

func CloseResp(resp *http.Response) {
	if resp != nil && resp.Body != nil {
		resp.Body.Close()
	}
}

func close(resp *http.Response) {
	if resp != nil && resp.Body != nil {
		resp.Body.Close()
	}
}

func Post2(url string, body string, headers map[string]string) *MyResponse {
	conn_http := GetHttpClient()
	logs.Debug("Send Post: %s\n", url)

	req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(body)))

	for k, v := range headers {
		req.Header.Set(k, v)
	}

	resp, err := conn_http.Do(req)

	if err != nil {
		logs.Error("Err2:", err, "body:", body)
		return nil
	}

	return convertMyResponse(resp, &err)
}

func UploadPost(url string, params map[string]string, headers map[string]string, paramName, path string) *MyResponse {
	conn_http := GetHttpClient()

	req, err := NewfileUploadRequest(url, params, paramName, path)

	for k, v := range headers {
		req.Header.Add(k, v)
	}

	resp, err := conn_http.Do(req)

	if err != nil {
		logs.Error("Err2:", err)
		return nil
	}

	return convertMyResponse(resp, &err)
}

// Creates a new file upload http request with optional extra params
func NewfileUploadRequest(uri string, params map[string]string, paramName, path string) (*http.Request, error) {

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)

	for key, val := range params {
		fmt.Println(key, val)
		_ = writer.WriteField(key, val)
	}

	part, err := writer.CreateFormFile(paramName, filepath.Base(path))
	if err != nil {
		return nil, err
	}

	fileData, _ := ioutil.ReadFile(path)
	_, err = io.Copy(part, bytes.NewReader(fileData))

	writer.Close()

	req, err := http.NewRequest("POST", uri, body)
	req.Header.Set("Content-type", writer.FormDataContentType())

	return req, err
}

func convertMyResponse(resp *http.Response, err *error) *MyResponse {
	myResp := NewMyResponse()
	myResp.StatusCode = resp.StatusCode

	body, _ := ioutil.ReadAll(resp.Body)
	myResp.Body = body

	for header := range resp.Header {
		//fmt.Println(header, ":", value)
		myResp.Headers[header] = resp.Header.Get(header)
	}

	return myResp
}

func md5Val(str string) string {
	h := md5.New()
	h.Write([]byte(str))
	return hex.EncodeToString(h.Sum(nil))
}

func Download(url string, dir string, rename string) (bool, string) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("[Download-ERROR]", err)
		}
	}()

	myResp := Get(url)

	if myResp == nil || len(myResp.Body) == 0 {
		return false, ""
	}

	imageName := rename
	if rename == "" {
		suffix := ".jpg"

		suffix = url[strings.LastIndex(url, "/"):]
		suffix = suffix[strings.Index(suffix, "."):]

		if strings.Index(suffix, ".gif") == 0 && len(suffix) > 4 {
			suffix = ".gif"
		}
		if strings.Index(suffix, ".jpg") == 0 && len(suffix) > 4 {
			suffix = ".jpg"
		}
		if strings.Index(suffix, ".png") == 0 && len(suffix) > 4 {
			suffix = ".png"
		}
		if strings.Index(suffix, ".mp4") == 0 && len(suffix) > 4 {
			suffix = ".mp4"
		}

		imageName = fmt.Sprint(md5Val(url), suffix)
	}

	if ok, _ := PathExists(dir); !ok {
		os.MkdirAll(dir, os.ModeDir)
		//os.Chdir(dir)
	}

	//os.MkdirAll(dir, os.ModeDir)

	file_path := fmt.Sprintf("%s/%s", dir, imageName)
	if ok, _ := PathExists(file_path); ok {
		os.Remove(file_path)
	}
	file, _ := os.Create(file_path)

	file.Write(myResp.Body)
	file.Close()

	return true, imageName
}
